package cn.thornbird.orgsync.service.impl;

import cn.thornbird.orgsync.entity.Attachment;
import cn.thornbird.orgsync.entity.City;
import cn.thornbird.orgsync.entity.Company;
import cn.thornbird.orgsync.exception.BadRequestException;
import cn.thornbird.orgsync.exception.ForbiddenException;
import cn.thornbird.orgsync.exception.InternalServerErrorException;
import cn.thornbird.orgsync.exception.NotFoundException;
import cn.thornbird.orgsync.model.PageList;
import cn.thornbird.orgsync.model.UserData;
import cn.thornbird.orgsync.model.combined.CityObject;
import cn.thornbird.orgsync.model.combined.CompanyObject;
import cn.thornbird.orgsync.repository.CityRepository;
import cn.thornbird.orgsync.repository.CompanyRepository;
import cn.thornbird.orgsync.repository.TaskRepository;
import cn.thornbird.orgsync.repository.specification.CompanySpecification;
import cn.thornbird.orgsync.service.AttachmentService;
import cn.thornbird.orgsync.service.CompanyService;
import cn.thornbird.orgsync.vo.record.CompanyRecord;
import cn.thornbird.orgsync.vo.request.CompanyQueryRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class CompanyServiceImpl extends BaseService<CompanyRepository, Company> implements CompanyService {

    private final CompanyRepository companyRepository;

    private final CityRepository cityRepository;

    private final TaskRepository taskRepository;

    private final AttachmentService attachmentService;

    @Override
    public CompanyObject find(String id) throws NotFoundException {
        Company company = findFirstById(companyRepository, id);
        return createCompanyObject(company);
    }

    @Override
    public PageList<CompanyObject> query(UserData userData, CompanyQueryRequest companyQueryRequest,
                                         Integer pageNumber, Integer pageSize) {
        Sort sort = Sort.by(Sort.Order.asc("createdAt"));
        Pageable pageable = PageList.generatePageable(pageNumber, pageSize, sort);
        Page<Company> page = companyRepository.findAll(new CompanySpecification(companyQueryRequest,
                userData.getUsername()), pageable);
        PageList<CompanyObject> pageList = PageList.createEmptyPageList(page);

        HashMap<String, City> cityMap = new HashMap<>();
        List<String> cityIds = new ArrayList<>();
        List<City> cities;
        for (Company company : page.getContent()) {
            if (company.getCityId() != null) {
                cityIds.add(company.getCityId());
            }
        }
        if (!cityIds.isEmpty()) {
            cities = cityRepository.findByIdIn(cityIds);
            for (City city : cities) {
                cityMap.put(city.getId(), city);
            }
        }

        HashMap<String, City> provinceMap = new HashMap<>();
        List<City> provinces = cityRepository.findProvinces();
        for (City province : provinces) {
            provinceMap.put(province.getId(), province);
        }

        for (Company company : page.getContent()) {
            CompanyObject companyObject = createCompanyObject(company, cityMap, provinceMap);
            pageList.getList().add(companyObject);
        }
        return pageList;
    }

    @Override
    public CompanyObject save(UserData userData, CompanyRecord companyRecord)
            throws BadRequestException, ForbiddenException, NotFoundException {
        Company company;
        checkDuplicateForCompany(companyRecord);
        if (companyRecord.getCityId() != null) {
            if (cityRepository.findFirstById(companyRecord.getCityId()) == null) {
                throw new NotFoundException("城市不存在");
            }
        }

        if (companyRecord.getId() == null) {
            checkAuthority(userData, null);
            company = new Company();
            companyRecord.setProperties(company);
            company.generateNewId();
        } else {
            company = findFirstById(companyRepository, companyRecord.getId());
            checkAuthority(userData, company);
            companyRecord.setProperties(company);
        }
        updateCompanyLogo(company, companyRecord);
        companyRepository.save(company);
        return createCompanyObject(company);
    }

    @Override
    public CompanyObject delete(UserData userData, String id)
            throws BadRequestException, ForbiddenException, NotFoundException {
        Company company = findFirstById(companyRepository, id);
        checkAuthority(userData, company);
        checkCompanyIsUsed(company);
        companyRepository.logicDelete(id);
        return createCompanyObject(company);
    }

    private void checkAuthority(UserData userData, Company company) throws ForbiddenException {
        if (company == null) {
            if (!userData.hasAuthority(UserData.ADMIN) && !userData.hasAuthority(UserData.AUTHORIZED)) {
                throw new ForbiddenException("禁止操作，没有权限");
            }
            return;
        }

        String creatorUsername = company.getCreatedBy() == null ? "" : company.getCreatedBy();
        if (!userData.hasAuthority(UserData.ADMIN)) {
            if (userData.hasAuthority(UserData.AUTHORIZED)) {
                if (!userData.getUsername().equals(creatorUsername)) {
                    throw new ForbiddenException("禁止操作，不是公司创建者");
                }
            } else {
                throw new ForbiddenException("禁止操作，没有权限");
            }
        }
    }

    private void checkCompanyIsUsed(Company company) throws BadRequestException {
        boolean isUsed = false;
        isUsed = taskRepository.countBySourceCompanyId(company.getId()) > 0 ||
                taskRepository.countByTargetCompanyId(company.getId()) > 0;
        if (isUsed) {
            throw new BadRequestException(REFERENCING_CURRENT_RECORD);
        }
    }

    private void checkDuplicateForCompany(CompanyRecord companyRecord) throws BadRequestException {
        Company company;
        if (companyRecord.getCityId() == null) {
            company = companyRepository.findFirstByNameIgnoreCaseAndCityIdIsNull(companyRecord.getName());
        } else {
            company = companyRepository.findFirstByCityIdAndNameIgnoreCase(companyRecord.getCityId(),
                    companyRecord.getName());
        }
        if (company != null) {
            String message = companyRecord.getCityId() == null ? "已存在相同名称的记录" : "已存在相同城市及名称的记录";
            if (companyRecord.getId() == null) {
                throw new BadRequestException(message);
            } else if (!companyRecord.getId().equals(company.getId())) {
                throw new BadRequestException(message);
            }
        }
    }

    private CompanyObject createCompanyObject(Company company) {
        CompanyObject companyObject = new CompanyObject();
        CityObject cityObject = null;
        if (company.getCityId() != null) {
            City city = cityRepository.findFirstById(company.getCityId());
            if (city != null) {
                cityObject = new CityObject();
                City parent = null;
                if (city.getParentId() != null) {
                    parent = cityRepository.findFirstById(city.getParentId());
                }
                cityObject.setCity(city);
                cityObject.setParent(parent);
            }
        }
        companyObject.setCompany(company);
        companyObject.setCity(cityObject);
        return companyObject;
    }

    private CompanyObject createCompanyObject(Company company, HashMap<String, City> cityMap,
                                              HashMap<String, City> provinceMap) {
        CompanyObject companyObject = new CompanyObject();
        CityObject cityObject = new CityObject();
        if (company.getCityId() != null && cityMap.containsKey(company.getCityId())) {
            City city = cityMap.get(company.getCityId());
            cityObject = new CityObject();
            cityObject.setCity(city);
            if (city.getParentId() != null) {
                if (provinceMap.containsKey(city.getParentId())) {
                    cityObject.setParent(provinceMap.get(city.getParentId()));
                }
            }
        }
        companyObject.setCompany(company);
        companyObject.setCity(cityObject);
        return companyObject;
    }

    private void updateCompanyLogo(Company company, CompanyRecord companyRecord) {
        if (companyRecord.getLogoId() == null) {
            if (companyRecord.getLogo() == null) {
                try {
                    Attachment oldAttachment = null;
                    if (company.getLogo() != null) {
                        oldAttachment = attachmentService.findByFileUrl(company.getLogo());
                    }
                    if (oldAttachment != null) {
                        attachmentService.delete(oldAttachment.getId());
                    }
                    company.setLogo(null);
                } catch (NotFoundException e) {
                    log.error("error", e);
                }
            }
        } else {
            try {
                Attachment attachment = attachmentService.find(companyRecord.getLogoId());
                Attachment oldAttachment = null;
                if (company.getLogo() != null) {
                    oldAttachment = attachmentService.findByFileUrl(company.getLogo());
                }
                if (oldAttachment != null) {
                    attachmentService.delete(oldAttachment.getId());
                }
                attachmentService.move(attachment.getId());
                company.setLogo(attachment.getFileUrl());
            } catch (NotFoundException | InternalServerErrorException e) {
                log.error("error", e);
            }
        }
    }

}
